#include "stdafx.h"
//#define BOOST_SPIRIT_DUMP_PARSETREE_AS_XML
#include "grammar.hpp"
#include <boost/spirit/tree/ast.hpp>
#include <boost/spirit/tree/tree_to_xml.hpp>

#include <iostream>
#include <stack>
#include <functional>
#include <string>
#include <cassert>

#if defined(BOOST_SPIRIT_DUMP_PARSETREE_AS_XML)
#include <map>
#endif

namespace UrDnd { namespace IrcBot { namespace Dicer
{
	using namespace std;
	using namespace boost::spirit;

	// Here's some typedefs to simplify things
	typedef char const*                              iterator_t;
	typedef tree_match<iterator_t>                   parse_tree_match_t;
	typedef parse_tree_match_t::const_tree_iterator  iter_t;

	using namespace System;
	using namespace System::Text;
	using namespace System::Collections::Generic;

	public ref class Calc : public IrcBot::ComponentModel::IAnswerer
	{
	public:
		Calc()
		{
			_randomizer = gcnew Random();

			_answerBuilder = gcnew StringBuilder();
			_verboseDiceOutput = false;
			_criticalSymbol = Char::ConvertFromUtf32(0x03);
			_resultSymbol = Char::ConvertFromUtf32(0x02);
		}

		virtual String^ Answer(UrDnd::IrcClient::Data^ data) 
		{
			List<String^ >^ answers = gcnew List<String^>();
			for each(String^ word in data->MessageEx)
				answers->Add(Run(word));
			
			StringBuilder^ result = gcnew StringBuilder();
			bool first = true;
			for each(String^ answer in answers)
			{
				if(answer == String::Empty)
					continue;

				if(first)
					first = false;
				else
					result->Append("; ");

				result->Append(answer);
			}

			result->Append(".");
			result->Insert(0, ": ");
			result->Insert(0, data->NickName);

			return first? String::Empty : result->ToString();
		}

		property String^ CriticalSymbol
		{
			String^ get() { return _criticalSymbol; }
			void set(String^ value) { _criticalSymbol = value; }
		}

		property bool VerboseDiceOutput
		{
			bool get() { return _verboseDiceOutput; }
			void set(bool value) { _verboseDiceOutput = value; }
		}

		property String^ ResultSymbol
		{
			String^ get() { return _resultSymbol; }
			void set(String^ value) { _resultSymbol = value; }
		}

	private:
		Random^ _randomizer;
		StringBuilder^ _answerBuilder;
		String^ _criticalSymbol;
		String^ _resultSymbol;
		bool _wasRepeater;
		bool _integerOnly;
		bool _wasDice;
		bool _verboseDiceOutput;
		bool _totalRequired;

		String^ Run(String^ textToParse)
		{
			DicerGrammar  calc; //  Our parser
#if defined(BOOST_SPIRIT_DUMP_PARSETREE_AS_XML)
			BOOST_SPIRIT_DEBUG_NODE(calc);
#endif

			_answerBuilder->Remove(0, _answerBuilder->Length);

			string str = (char *) System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(textToParse).ToPointer();
			tree_parse_info<> info = ast_parse(str.c_str(), calc);

			if (!info.full)
				return String::Empty;

			dump_tree(info, str);
			bool printed = false;
			_wasRepeater = _wasDice = _totalRequired = false;
			_integerOnly = true;

			long result;
			
			if(eval_expression(info.trees.begin(), printed, result) && !_integerOnly)
			{
				if(!printed)
					_answerBuilder->Append(result);

				if(!_wasRepeater && _totalRequired)
					_answerBuilder->AppendFormat("={1}{0}{1}", result, _resultSymbol);

				if(_wasDice)
				{
					_answerBuilder->Insert(0, ") ");
					_answerBuilder->Insert(0, textToParse);
					_answerBuilder->Insert(0, "(");
				}

				return _answerBuilder->ToString();
			}
			else
			{
				return String::Empty;
			}
		}

		void dump_tree(const tree_parse_info<> info, string str)
		{
#if defined(BOOST_SPIRIT_DUMP_PARSETREE_AS_XML) 
			std::map<parser_id, std::string> rule_names;
			rule_names[DicerGrammar::integerID] = "integer";
			rule_names[DicerGrammar::factorID] = "factor";
			rule_names[DicerGrammar::termID] = "term";
			rule_names[DicerGrammar::expressionID] = "expression";
			rule_names[DicerGrammar::diceID] = "dice";
			tree_to_xml(cout, info.trees, str.c_str(), rule_names);
#endif
		}

		bool eval_expression(iter_t const& i, bool& printed, long& result)
		{
			bool valuePrinted = false;
			
			if (i->value.id() == DicerGrammar::integerID)
			{
				// convert string to integer
				string integer(i->value.begin(), i->value.end());
				
				result = strtol(integer.c_str(), 0, 10);

				// overflow
				if(result == LONG_MAX || result == LONG_MIN)
					return false;
				
				//underflow
				if(result == 0 && integer.size() != 0)
					return false;

				// seems to be ok
				return true;
			}
			
			if (i->value.id() == DicerGrammar::factorID)
			{
				if(i->children.size() == 3)
				{
					_answerBuilder->Append("(");
					
					if(eval_expression(i->children.begin() + 1, valuePrinted, result))
					{
						if(!valuePrinted)
							_answerBuilder->Append(result);

						_answerBuilder->Append(")");
					
						printed = true;
						return true;
					}
					else
						return false;
				}

				if (*i->value.begin() == '-')
				{
					_answerBuilder->Append("-");
					if(eval_expression(i->children.begin() + 1, valuePrinted, result))
					{
						if(!valuePrinted)
							_answerBuilder->Append(result);

						printed = true;

						result *= -1;

						return true;
					}
				}

				if (*i->value.begin() == '+')
				{
					_answerBuilder->Append("+");
					if(eval_expression(i->children.begin(), valuePrinted, result))
					{
						if(!valuePrinted)
							_answerBuilder->Append(result);

						printed = true;

						return true;
					}
					else
					{
						return false;
					}
				}

				//throw gcnew System::InvalidOperationException("Incorrect factor");
				return false;
			}
			
			if (i->value.id() == DicerGrammar::termID)
			{
				_integerOnly = false;
				_totalRequired = true;
				if (*i->value.begin() == '*')
				{
					printed = true;
					return evalBinaryOperator< multiplies<long> >(i, "*", result);
				}
				
				if (*i->value.begin() == '/')
				{
					printed = true;
					return evalBinaryOperator< divides<long> >(i, "/", result);
				}

				//throw gcnew System::InvalidOperationException("There should be * or /");
				return false;
			}
			
			if (i->value.id() == DicerGrammar::expressionID)
			{
				_integerOnly = false;
				_totalRequired = true;
				if (*i->value.begin() == '+')
				{
					printed = true;
					return evalBinaryOperator< plus<long> >(i, "+", result);
				}
				
				if (*i->value.begin() == '-')
				{
					printed = true;
					return evalBinaryOperator< minus<long> >(i, "-", result);
				}

				//throw gcnew System::InvalidOperationException("There should be + or -");
				return false;
			}

			if (i->value.id() == DicerGrammar::repeaterID)
			{
				_integerOnly = false;
				long repeatCount;
				result = 0;
				if(eval_expression(i->children.begin(), valuePrinted, repeatCount))
				{
					bool first = true;
					for(long repeatIndex = 0; repeatIndex < repeatCount; repeatIndex++)
					{
						if(first)
							first = false;
						else
							_answerBuilder->Append(", ");

						long repeatResult;
						
						if(!eval_expression(i->children.begin() + 1, valuePrinted, repeatResult))
							return false;

						if(!valuePrinted)
							_answerBuilder->AppendFormat("{0}", repeatResult);

						if(_totalRequired)
							_answerBuilder->AppendFormat("={0}", repeatResult);
					}

					printed = true;
					_wasRepeater = true;
					return true;
				}

				return false;
			}

			if (i->value.id() == DicerGrammar::diceID)
			{
				_integerOnly = false;
				if (i->children.size() == 1)
				{
					bool wasIntegerParseSuccessful = eval_expression(i->children.begin(), valuePrinted, result);
					_answerBuilder->AppendFormat("{0}", result);
					printed = true;
					return wasIntegerParseSuccessful;
				}

				long diceCount;
				long diceNominal;

				if(!(eval_expression(i->children.begin(), valuePrinted, diceCount) &&
					eval_expression(i->children.begin() + 1, valuePrinted, diceNominal)))
					return false;

				if (LONG_MAX / diceCount < diceNominal)
					return false;

				long minCritRange = diceCount * diceNominal;
				long maxCritRange = diceCount * diceNominal;

				if (i->children.size() == 4)
				{
					if(!(eval_expression(i->children.begin() + 2, valuePrinted, minCritRange) &&
						eval_expression(i->children.begin() + 3, valuePrinted, maxCritRange)))
						return false;
				}

				result = 0;
				vector<long> dices(diceCount);
				for(long diceIndex = 0; diceIndex < diceCount; diceIndex++)
				{
					dices[diceIndex] = _randomizer->Next(diceNominal) + 1;
					result += dices[diceIndex];
				}

				if(minCritRange <= result && result <= maxCritRange)
					_answerBuilder->AppendFormat("{1}03{0}{1}", result, _criticalSymbol);
				else
					_answerBuilder->AppendFormat("{0}", result);

				if(_verboseDiceOutput)
				{
					_answerBuilder->Append("[");
					for(long diceIndex = 0; diceIndex < diceCount; diceIndex++)
						_answerBuilder->AppendFormat("{0},", dices[diceIndex]);
					
					if(diceCount > 0)
						_answerBuilder->Remove(_answerBuilder->Length - 1, 1);

					_answerBuilder->Append("]");
				}


				printed = true;

				_wasDice = true;
				return true;
			}

			return false;
		}

		template<class Action>
		bool evalBinaryOperator(iter_t op, String^ opString, long& result)
		{
			bool leftPrinted = false;
			long left;
			bool rightPrinted = false;
			long right;

			if (eval_expression(op->children.begin(), leftPrinted, left))
			{
				if(!leftPrinted)
					_answerBuilder->AppendFormat("{0}", left);
				
				_answerBuilder->AppendFormat("{0}", opString);

				if(eval_expression(op->children.begin()+1, rightPrinted, right))
				{
					if(!rightPrinted)
						_answerBuilder->AppendFormat("{0}", right);

					Action action;
					result = action(left, right);

					return true;
				}
			}

			return false;
		}
	};
}}} // namespace UrDnd::Bot::Dicer